package my.mark.mybaibaoxiang.juc.biji01.jmm;

/**
 * volatile的可见性：线程A改变initFlag属性之后，线程B马上感知到
 */
public class VolatileVisibilitySample {
    //TODO 在这个共享变量加volatile关键字，可以让该变量变化时立马被别的线程嗅探到。
    /**
     * volatile是一个特征修饰符（type specifier）。
     * volatile的作用是作为指令关键字，确保本条指令不会因编译器的优化而省略，且//TODO 要求每次直接读值。
     * volatile的变量是说这变量可能会被意想不到地改变，这样，编译器就不会去假设这个变量的值了。
     */
    private volatile boolean initFlag = false;
    static Object object = new Object();

    public void refresh(){
        this.initFlag = true; //普通写操作，(相对于volatile写)
        String threadname = Thread.currentThread().getName();
        System.out.println("线程："+threadname+":修改共享变量initFlag");
    }

    public void load(){
        String threadname = Thread.currentThread().getName();
        int i = 0;
        while (!initFlag){
            //线程在此处空跑，等待initFlag状态改变
            /**
             * 如果此处不加synchronized同步代码块，则该线程可能会一直在跑，优先级比较高，别的线程抢不到他的cpu资源。
             * TODO 如果此处加该同步代码块，initFlag值变化也会被感知到，
             * 但原因是同步代码块有可能产生竞争和阻塞，导致两个线程上下文切换的时候，重新读取了initFlag的值。
             */
            synchronized (object){
                i++;
            }
            //i++;
        }
        System.out.println("线程："+threadname+"当前线程嗅探到initFlag的状态的改变"+i);
    }

    public static void main(String[] args){
        VolatileVisibilitySample sample = new VolatileVisibilitySample();
        Thread threadA = new Thread(()->{
            sample.refresh();
        },"threadA");

        Thread threadB = new Thread(()->{
            sample.load();
        },"threadB");

        threadB.start();
        try {
             Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadA.start();
    }

}
